#include "RtBase.h"
#include "RtDetectionConnector.h"

///////////////////////////////////////////
//class CRtDetectionConnector
///////////////////////////////////////////
CRtDetectionConnector::CRtDetectionConnector()
{
	m_wPriority = TOP_PRIORITY;
	//m_bGetHighestPriority = FALSE;
	m_aSink = NULL;
	
	m_dwConnFailedCnt = 0;
	m_dwConnAddCnt = 0;
}

CRtDetectionConnector::~CRtDetectionConnector()
{
	m_aSink = NULL;

	CancelConnect();
//	m_conn_list.clear();
}

BOOL CRtDetectionConnector::IsConnector()
{
	return TRUE;
}

RtResult CRtDetectionConnector::AddConnection(
											  CRtConnectionManager::CType Type, 
											  const CRtInetAddr &aAddrPeer,
											  CRtTimeValue *aTimeDelay)
{
	CRtAutoPtr<IRtConnector> pTPConnector;
	RtResult rv = CRtConnectionManager::Instance()->CreateConnectionClient(
		Type, 
		pTPConnector.ParaOut());
	
	if(RT_SUCCEEDED(rv))
	{
		CRtAutoPtr<CConnectorItem> pConnItem(new CConnectorItem(
			pTPConnector.ParaIn(),
			Type, 
			m_wPriority++, 
			aAddrPeer,
			this,
			aTimeDelay));
		
		m_conn_list.push_back(pConnItem);
	}
	
	return rv;		
}

void CRtDetectionConnector::StartDetectionConnect(
		IRtAcceptorConnectorSink *aSink,
		CRtTimeValue *aTimeout,
		DetectMode inMode,
		CRtInetAddr *aAddrLocal)
{
	RT_ASSERTE(!m_conn_list.empty());
	
	if (aTimeout) 
		m_timeout = *aTimeout;
	if (aAddrLocal)
		m_localAddr = *aAddrLocal;
	
	m_dwConnAddCnt = m_conn_list.size();
	m_aSink = aSink;
	m_dwConnFailedCnt = 0;

	for (iter_type it=m_conn_list.begin(); it!=m_conn_list.end(); ++it)
	{
		(*it)->AsycConnect( 
			(m_timeout==CRtTimeValue::s_tvZero) ? NULL : &m_timeout, 
			(m_localAddr.GetIpAddrIn4Bytes()==INADDR_ANY) ? NULL : &m_localAddr);
		if (inMode==DETECT_INTERVAL_1S)
		{
			if (m_dwConnAddCnt>1){
				CRtTimeValue timevl((double)1L);
				m_Timer.Schedule(this, timevl, m_dwConnAddCnt-1);
			}
			break;
		}
	}
}

void CRtDetectionConnector::OnTimer(CRtTimerWrapper* id)
{
	iter_type it_stop = m_conn_list.begin();
	iter_type it_end = m_conn_list.end();
	for(; it_stop != it_end; it_stop++)
	{
		if(!(*it_stop)->IsConnected())
		{
			(*it_stop)->AsycConnect( 
				(m_timeout==CRtTimeValue::s_tvZero) ? NULL : &m_timeout, 
				(m_localAddr.GetIpAddrIn4Bytes()==INADDR_ANY) ? NULL : &m_localAddr );
			break;
		}
	}
}

void CRtDetectionConnector::AsycConnect(
										IRtAcceptorConnectorSink *aSink,
										const CRtInetAddr &aAddrPeer, 
										CRtTimeValue *aTimeout,
										CRtInetAddr *aAddrLocal)
{
	//Cannot call this function here.
//	RT_ASSERTE(FALSE);
// 	RT_ASSERTE(!aAddrLocal);

	iter_type it_stop = m_conn_list.begin();
	iter_type it_end = m_conn_list.end();
	for(; it_stop != it_end; it_stop++)
		(*it_stop)->SetAddrPeer(aAddrPeer);

	// TimeDelay should be set before calling this function.
	StartDetectionConnect(aSink, aTimeout, DETECT_INTERVAL_1S, aAddrLocal);
}

void CRtDetectionConnector::CancelConnect()
{
	m_Timer.Cancel();
	if(m_conn_list.empty())
		return;
	
	iter_type it_stop = m_conn_list.begin();
	iter_type it_end = m_conn_list.end();
	
	for(; it_stop != it_end; it_stop++)
	{
		(*it_stop)->CancelConnect();
//		if((*it_stop)->GetStatus() == CONNECTOR_STATUS_CONNECTED)
//		{
//			if((*it_stop)->m_pITransport)
//				(*it_stop)->m_pITransport->Disconnect((RtResult)RT_OK);
//		}
//
//		(*it_stop)->m_pConnector->CancelConnect();
//		(*it_stop)->SetStatus(CONNECTOR_STATUS_UNCONNECTED);
	}
}

void CRtDetectionConnector::CancelConnect(CConnectorItem* pExclude)
{
	m_Timer.Cancel();
	if(m_conn_list.empty())
		return;
	
	iter_type it_stop = m_conn_list.begin();
	iter_type it_end = m_conn_list.end();
	
	for(; it_stop != it_end; it_stop++)
	{
		if((*it_stop).Get() == pExclude)
			continue;
		(*it_stop)->CancelConnect();

//		if((*it_stop)->GetStatus() == CONNECTOR_STATUS_CONNECTED)
//		{
//			if((*it_stop)->m_pITransport)
//				(*it_stop)->m_pITransport->Disconnect((RtResult)RT_OK);
//		}
//
//		(*it_stop)->m_pConnector->CancelConnect();
//		(*it_stop)->SetStatus(CONNECTOR_STATUS_UNCONNECTED);
	}
}

void CRtDetectionConnector::OnConnectFailed()
{
	OnTimer( NULL);	
}

/////////////////////////////////////////////////////////
//Inner class CRtDetectionConnector::CConnectorItem
/////////////////////////////////////////////////////////
CRtDetectionConnector::CConnectorItem::CConnectorItem(
													  IRtConnector *pIRtConnector,
													  CRtConnectionManager::CType aType, 
													  WORD wPriority, 
													  CRtInetAddr aAddrPeer, 
													  CRtDetectionConnector* pOuterClass,
													  CRtTimeValue* aTimeDelay )
{
	m_pConnector = pIRtConnector;
	m_aType = aType;
	m_wPriority = wPriority;
	m_aAddrPeer = aAddrPeer;
	
	m_pOuterClass = pOuterClass;
	
	m_wStatus = CONNECTOR_STATUS_UNCONNECTED;
	
	//RT_ASSERTE( aTimeDelay );
	//m_TimeDelay = *aTimeDelay;
	m_bStart = FALSE;
}

CRtDetectionConnector::CConnectorItem::~CConnectorItem()
{
	//Empty
}

void CRtDetectionConnector::CConnectorItem::AsycConnect(
		CRtTimeValue *aTimeout,
		CRtInetAddr *aAddrLocal)
{
	m_pConnector->AsycConnect(
		this,
		m_aAddrPeer, 
		aTimeout,
		aAddrLocal);
	m_bStart = TRUE;
}

void CRtDetectionConnector::CConnectorItem::CancelConnect()
{
	/*if(m_pITransport)
	m_pITransport->Disconnect((RtResult)RT_OK);*/
	
	m_pITransport = NULL;
	m_pConnector->CancelConnect();
	SetStatus(CONNECTOR_STATUS_UNCONNECTED);
	m_bStart = FALSE;
}

void CRtDetectionConnector::CConnectorItem::IsAllFailed(RtResult aReason)
{
	//RT_INFO_TRACE_THIS("CRtDetectionConnector::CConnectorItem::IsAllFailed, FAILECNT="<<m_pOuterClass->m_dwConnFailedCnt<<",try number="<<m_pOuterClass->m_dwConnAddCnt<<", m_pOuterClass="<<m_pOuterClass);
	if(++(m_pOuterClass->m_dwConnFailedCnt) 
		== (m_pOuterClass->m_dwConnAddCnt))//All connections failed
	{
		m_pOuterClass->CancelConnect();

		if(m_pOuterClass->m_aSink)
		{
			m_pOuterClass->m_aSink->OnConnectIndication(
				aReason,
				NULL,
				m_pOuterClass);	//Callback to Upper layer
		}
	}
}

void CRtDetectionConnector::CConnectorItem::OnConnectIndication(
																RtResult aReason,
																IRtTransport *aTrpt,
																IRtAcceptorConnectorId *aRequestId)
{
	m_aReason = aReason;
	m_pITransport = aTrpt;

	RT_ASSERTE(m_pConnector.Get() == aRequestId);
	
	if(RT_FAILED(aReason))
	{
		IsAllFailed(aReason);
		return;
	}
	
	if(m_pOuterClass->m_aSink)
	{
		m_pOuterClass->m_aSink->OnConnectIndication(
			m_aReason,
			m_pITransport.ParaIn(),
			m_pOuterClass);	//Callback to Upper layer
	}

	// Cancel all Connect except this
	m_pOuterClass->CancelConnect(this);
}

